package vn.edu.hcmut.cse.ai.search.problems.blocksworld;

import java.util.LinkedList;
import java.util.List;
import vn.edu.hcmut.cse.ai.infrastructure.Action;
import vn.edu.hcmut.cse.ai.infrastructure.Actions;
import vn.edu.hcmut.cse.ai.search.problems.blocksworld.actiontype.Pickup;
import vn.edu.hcmut.cse.ai.search.problems.blocksworld.actiontype.Putdown;
import vn.edu.hcmut.cse.ai.search.problems.blocksworld.actiontype.Stack;
import vn.edu.hcmut.cse.ai.search.problems.blocksworld.actiontype.Unstack;
import vn.edu.hcmut.cse.ai.search.problems.blocksworld.predicate.ArmEmpty;
import vn.edu.hcmut.cse.ai.search.problems.blocksworld.predicate.Clear;
import vn.edu.hcmut.cse.ai.search.problems.blocksworld.predicate.OnTable;

public class BlocksWorldActions implements Actions{

    // Preallocate some variables for faster checking
    private static final ArmEmpty armEmptyPred = new ArmEmpty();
    private static final OnTable onTablePred = new OnTable(-1);
    
    @Override
    public List<Action> getActionList(Object state) {
        List<Action> actionList = new LinkedList<>();
        BlocksWorldState blocksWorldState = (BlocksWorldState)state;
        
        // Check if the robot arm is holding or empty
        if (blocksWorldState.isPreconditionAccepted(BlocksWorldActions.armEmptyPred)) {
            // Will have a pickup or unstack action
            // Only clear blocks can be picked up or unstacked
            // Each state has a list of hash set to store the clear blocks
            for (Clear clearPred : blocksWorldState.getClearPredicates()) {
                int block = clearPred.getBlock();
                // Check if this block is on table or on another block
                BlocksWorldActions.onTablePred.setBlock(block);
                if (blocksWorldState.isPreconditionAccepted(BlocksWorldActions.onTablePred)) {
                    // ARMEMPTY + CLEAR + ONTABLE --> PICKUP
                    BlocksWorldAction action = new BlocksWorldAction(new Pickup(block));
                    actionList.add(action);
                } else {
                    // Because the block is not on table, it should be on another block
                    int underBlock = blocksWorldState.getOnMap().get(block);
                    BlocksWorldAction action = new BlocksWorldAction(new Unstack(block, underBlock));
                    actionList.add(action);
                }
            }
        } else {
            // The robot is holding some block
            // It can put down the block to the table or stack it on some clear block
            int holdingBlock = blocksWorldState.getHoldingBlock();
            
            // Put down
            BlocksWorldAction putDownAction = new BlocksWorldAction(new Putdown(holdingBlock));
            actionList.add(putDownAction);
            
            // Add stack actions
            for (Clear clearPred : blocksWorldState.getClearPredicates()) {
                int block = clearPred.getBlock();
                if (block != holdingBlock) {    // The hold block is also clear
                    BlocksWorldAction action = new BlocksWorldAction(new Stack(holdingBlock, block));
                    actionList.add(action);
                }                
            }
        }
        
        return actionList;
    }
    
}
